# Media Player Info Script

The idea is to display the currently playing song in your panel.  
[Tint2][1] has an Executor plugin that will display any command's output -
other panels also have this functionality.  
There are some formatting options.  
Additionally this script can

- display a notification on song change
- update a file that contains some information:
	- the player's state play/pause/stop/unknown as defined in the state_nice array in the main script
	- the title of the currently playing song, usually 'Song Name - Artist' or, failing that, file name
	- the file (usually full path; URL for streams)
	- the time position in the song as elapsed/total, or merely elapsed for streams
	- chosen player's play/pause toggle command
	- chosen player's next song command
	- chosen player's previous song command
	- chosen player's show window command
	- full path to currently playing song's album art
	- name of the chosen player according to the list of players
- keep a history of played tracks
- "Upvote" a track by adding a symbol next to its history entry

The focus is on `mocp` (a.k.a. MOC a.k.a. Music on Console), a simple,
directory based music player.  
Plugins for Audacious and MPD have been added.  
Adding new plugins should be easy - refer to the files inside the `plugins` folder.

Screenshot of [tint2][1] runnig mpi-panel inside its Executor plugin
(notification on song change provided by [dunst][6]):

![scrot][3]

[Screenshot with tooltip][2]  
[Screenshot with artwork as panel icon][4]  
[GIF screencast with multiple players][5]

Please refer to `mpi-panel -h` for further information:

```
tl;dr
Called without arguments, this script returns the title (filled as intelligently
as possible, whether it's a network stream or a file without metadata) of the
currently playing song to stdout, clipped and ellipsized to a width of 60 chars,
and a more verbose tooltip to stderr.
If you don't use this script with tint2, you might want to discard the tooltip:
mpi-panel 2>/dev/null

Features

Query a list of audio players for currently playing song info.
This list defaults to builtin capabilities + all plugin files found inside the
plugin directory. Identically named plugins override builtins.
If this query gets multiple results, make one choice based on certain rules (see
-p, -P and -S) and format the output to stdout.

The output format is fairly limited; if you need more finegrained control you
should write your own plugin (one example is provided) or use the media player's
own functionality. Instead, the idea is to

 - make best use of the (limited) horizontal space on your panel
 - not be limited to 1 media player (incl. playback control)

mpi-panel is made with tint2's executor plugin in mind (but in no way limited
to it) and caters to most of its features:

 - show an icon in addition to the text (optional)
 - continuous operation (optional)
 - tooltip is sent to stderr
 - custom actions for clicking in the executor (left/right/middle/wheel)

Formatting options

 a) shortening to a max. length (default: 60) and ellipsizing (see -w and -e)
 b) folding output into more than 1 line, respecting word boundaries (see -l)

- which happens in exactly that order. The idea is to use the output in a
horizontal panel, where the number of lines is fixed, and horizontal space is
limited but not fixed. A third formatting option is to

 c) prepend a line with the path to an icon representing current player state
    (play/pause/stop). This has tint2's executor plugin in mind. See -i and -s.

Full information (still formatted but not ellipsized, one line for each player
on the list & running) is output to stderr (e.g. as tooltip for tint2's
executor plugin).

Supported players
    mpd audacious mocp (see -p for full description)

Plugins
    Please read README.md inside the plugins directory

Options
    -p str   Media players to query. A space-separated list of commands that can
             be queried by 'pgrep -x'. This can include both builtin functions
             and plugins. If this option is specified, only the players listed
             will be queried, in that order. Plugins not on the list won't even
             be loaded. Then the script will stick to the first player that is
             playing AND returns a usable title string.
             This string is not checked against the list of default players,
             to save on filesystem queries and sanity checks.
             Default if not specified: "mpd audacious mocp"

    -P       If no player is currently playing, consider information from a
             paused player as well, according to the logic described above.

    -S       Include Stopped as a third option in the logic above. Implies -P.

    -w int   maximum length of the complete string before it is ellipsized or
             split into lines with -l. Default: 60 - Must be at least 10.

    -e str   String to use to ellipsize output that is too long. Default: "…"

    -r str   String to use to separate artist from song title, and the
             tooltip elements. Default: " - "
             This is also used inside the history file (see -H).
             Please keep in mind that network streams usually hardcode their
             titles to "Artist - SongTitle", no matter what you define here.

    -l int   Output will be split into this many lines, respecting word
             boundaries. Default: 1 - Must be between 1 and 100.

    -i dir   Path to a directory containing icons to display, with these names:
             Playing Paused Stopped StateUnknown. 
             Specifying this option makes the script output a first line with
             the path to the icon representing the current state. This is made
             to work with the tint2 Exec plugin, but can be utilized in other
             contexts, too.
             If an icon does not exist, the text "no icon" is returned.
             Formats: png svg gif PNG jpg jpeg GIF JPG JPEG

    -s str   Space-separated list of strings to name the 4 player states.
             Defaults to "Playing Paused Stopped StateUnknown".
             These strings also provide the basenames for the icons.
             All 4 strings are mandatory, even if not all icons are present.

    -T       Append elapsed or remaining time (depends on the plugin, but all
             the builtins use elapsed), and total time where applicable.

    -c int   Will run the script in a continuous loop with int seconds delay
             between updates. Minimum 1s, only whole seconds. Not exact - the
             delay is added to the time it takes to get the information.

    -t int   "tint2 mode". Like -c, it will run the script in a continuous
             loop with int seconds delay between updates. Minimum 1s, only whole
             seconds. Then it does 2 more things:
                1. Return the exact number of lines requested, even if it means
                   outputting empty lines, and even if nothing at all would be
                   output otherwise.
                2. Start each loop by sending a clearscreen sequence to stderr

    -d path  Dump information to this file (absolute path please), each bit of
             information on its own line: chosen player's play/pause toggle
             command, chosen player, player state, song title, time played, song
             length, filename.
                - the player's state play/pause/stop/unknown as defined in the
                  state_nice array in the main script
                - the title of the currently playing song, usually 
                  'Song Name - Artist' or, failing that, file name
                - the file (usually full path; URL for streams)
                - the time position in the song as elapsed/total, or merely
                  elapsed for streams
                - chosen player's play/pause toggle command
                - chosen player's next song command
                - chosen player's previous song command
                - chosen player's show window command
                - full path to currently playing song's album art
                - name of the chosen player according to the list of players

    -D       If -d is specified, the show window command dumped will always
             be for the first player on the list.

    -a       Alternative command to run (and show output of) when no usable
             output string is available. if -d is also active the dump will
             contain data for the first player on the list.
             No quality control! Be careful what you enter here!

    -n       Send notification on song change. Only works when -d is also
             specified. If album art exists, that will be used as an icon,
             otherwise "audio-speakers-symbolic".

    -y int   Delay in seconds before notification is shown. Especially
             mocp (Music On Console) changes this too early with network
             streams.

    -H path  Save history to this file (updated on song change). Only works
             when -d is also specified.

    -U       "Upvote" current song. This simply adds a string to the end of the
             newest history entry. Multiple "upvotes" are possible. You need to
             specify "-H path" too if you want to use this.

    -u str   Which string to use for "upvoting". Default: "☻" (a simple smiley)

    -A       Look for artwork images in local folders. Default string to search
             for: "folder cover album front art" (case insensitive), plus the
             exact name of the file playing, without extension. Same extensions
             as for -i.
             Resulting path will be used with -d and -n.
             To make this work with mpd, -M must also be specified.
             Since this is a slightly more expensive process, it will only be
             performed for the chosen + playing song.

    -I       Implies -A. The art found will be used as an icon string.
             Specifying this option makes the script output a first line with
             the path to the artwork image. This is made to work with the tint2
             Exec plugin, but can be utilized in other contexts, too.
             Artwork is searched as follows:
              - any combination of "folder cover album front art" (case
                insensitive), "*", "png svg gif PNG jpg jpeg GIF JPG JPEG"
              - an image with the same name as the file playing
              - any image in the folder
              - try to extract "folder.jpg" from the file with ffmpeg

             If no artwork is found, but -i is defined, an icon for the
             current playback state is shown. Otherwise no icon is shown.
             To make this work with mpd, -M must also be specified.

    -M str   For mpd only: path to music directory.

    -1       Will make (fairly) sure only one script instance is running.

    -h       Display this message


Bugs and Limitations
    The multiline functionality (-l) is not very precise.

    Querying multiple players while multiple players are running works as
    defined; nevertheles the result might be confusing, as is the developer of
    this script from excessive testing.

    Of course there might be more bugs.
    Please open an issue if you have anything to say :)

```

### Installation

The script needs to find files in its directory: the help text, and the plugins.
Please do not copy the script itself elsewhere, instead make a symlink. Example:

    cd ~/bin; ln -s /path/to/media-player-info/mpi-panel

### Dependencies

It's a bash script, written on version 4.4.12. It relies almost completely on
builtins, except for `pgrep` (procps-ng), `touch` and `readlink` (GNU
coreutils) and optionally `less` (only for the help text, the same as in this
README).  

That is, apart from the media player queries of course: mpd relies on `mpc`,
audacious relies on `audtool`, mocp is self-sufficient.

Some media players require a terminal to launch a user interface (mocp, mpd).
If you use the "dump" option (-d), you can then, amongst other things, access a
command to launch the UI. For this it is good to set the variable `TERMINAL` to
your favorite terminal emulator, otherwise the script defaults to `xterm`. If 
you don't want this variable in your environment permanently, you can launch
the script like this:

    TERMINAL=fancy-terminal mpi-panel

### Plugins

One of the main points of this script is to separate all player-specific functionality
into one dedicated function per player. These can be loaded externally as plugins.  
It is easy to provide your own plugins.  
Identically named plugins override builtins, so costumisation is possible.

Please read the README.md inside the plugins directory.

### Example configuration snippet for tint2's Executor Plugin

```
execp = new
execp_command = mpi-panel -w80 -t2 -p"mocp" -d/tmp/tint2-mpi-panel-dump -H$HOME/.moc/mpi-panel-history -a"sed -n 10p /tmp/tint2-mpi-panel-dump"
execp_interval = 2
execp_continuous = 1
execp_has_icon = 0
execp_cache_icon = 0
execp_icon_h = 24
execp_font = sans 13
execp_font_color = #ffffff 90
execp_markup = 0
execp_background_id = 2
execp_centered = 0
execp_padding = 8 0 8
execp_lclick_command = $(sed -n 5p /tmp/tint2-mpi-panel-dump)
execp_mclick_command = $(sed -n 8p /tmp/tint2-mpi-panel-dump)
execp_rclick_command = $(sed -n 6p /tmp/tint2-mpi-panel-dump)
execp_uwheel_command = mpi-panel -H$HOME/.moc/mpi-panel-history -U
execp_dwheel_command = urxvt -e sh -c 'nano $HOME/.moc/mpi-panel-history'
```

[1]: https://gitlab.com/o9000/tint2/blob/master/doc/tint2.md
[2]: http://dt.iki.fi/stuff/mpi-panel/mpi-panel-tooltip.png
[3]: http://dt.iki.fi/stuff/mpi-panel/mpi-panel-notification.png
[4]: http://dt.iki.fi/stuff/mpi-panel/mpi-panel-icon.png
[5]: http://dt.iki.fi/stuff/mpi-panel/mpi-panel.gif
[6]: https://dunst-project.org/
